/**
 * Author:seebin
 * 
 * 快速生成开发文件
 * 
 * 使用方法:
 * 生成module
 *   npm run g <目录>
 * 生成no-model
 *   npm run g <目录> --no-module
 * 
 * 支持多级目录生成
 *   npm run g test/detail
 * 
 * 小提示:
 *   最后一个目录名字将会作为相关文件的名字, 只需要关注目录即可, 不需要关注文件名,不过我们建议将复词文件名以中划线分割,
 *   比如: npm run g member/member-list
 * 
 *   不会更新 db.constants.ts 文件  需要开发者视情况添加/修改
 * 
 *   不会更新上级module文件  需要手动更新
 * 
 *   命令执行完,最好确认下文件的正确性
 */

const fs = require('fs');
const basePath = 'src';

// 要创建的文件路径
let pathInput = process.argv[2];
if (!pathInput) {
  throw Error('未指定文件目录名 你可能是需要:yarn g <dir>, 或者查看脚本使用说明')
}

// 生成文件
const initFile = function (muLu, content) {
  let dirArray = muLu.split("/");
  dirArray.forEach(function (value, i) {
    i++;
    if (i === dirArray.length) {
      if (!!value.match(/\./g)) {
        if (!!fs.existsSync(muLu)) {
          return;
        } else {
          if (fs.writeFileSync(muLu, content, 'utf8')) {
            return console.log("FAILED  " + muLu);
          } else {
            return console.log("CREATE  " + muLu);
          }
        }
      }
    }
    const partPath = dirArray.slice(0, i).join("/");
    if (!!fs.existsSync(partPath)) {

    } else {
      fs.mkdirSync(partPath);
    }
  });
};

const dirArray = pathInput.split("/");
// 取出最后的目录名
const fileName = dirArray[dirArray.length - 1];

// 将文件名添加到路径上
const newPathInput = pathInput + '/' + fileName;

let className = '';
if (fileName.indexOf('-') > 0) {
  // 文件名包含-
  let nameArray = fileName.split('-');
  nameArray.forEach((item) => {
    className += item.replace(item[0], item[0].toUpperCase());
  })
} else if (fileName.indexOf('_') > 0) {
  // 文件名包含_
  let nameArray = fileName.split('_');
  nameArray.forEach((item) => {
    className += item.replace(item[0], item[0].toUpperCase());
  })
} else {
  className = fileName.replace(fileName[0], fileName[0].toUpperCase());
}

const moduleClassName = className + 'Module';
const controllerClassName = className + 'Controller';
const serviceClassName = className + 'Service';
const dtoClassName = className + 'Dto';
const interfaceClassName = className;
const providersClassName = className + 'Providers';
const schemaClassName = className + 'Schema';

// name.module.ts
const moduleFile = `import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';

import { DatabaseModule } from '@/database/database.module';

import { ${controllerClassName} } from './${fileName}.controller';
import { ${serviceClassName} } from './${fileName}.service';
import { ${providersClassName} } from './schema/${fileName}.providers';

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    DatabaseModule
  ],
  controllers: [${controllerClassName}],
  providers: [${serviceClassName}, ...${providersClassName}]
})
export class ${moduleClassName} { }
`;

// name.controller.ts
const controllerFile = `import { Controller, UseGuards, Body, Post } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiUseTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';

import { ${dtoClassName} } from './dto/${fileName}.dto';

@ApiUseTags('某某模块')
@ApiBearerAuth()
@Controller('${fileName}')
@UseGuards(AuthGuard())
export class ${controllerClassName} {

  @Post('')
  @ApiOperation({
    title: '接口名称',
    description: '接口描述'
  })
  async search(@Body() param: ${dtoClassName}) {
    return;
  }
}
`;

// name.service.ts
const serviceFile = `import { Injectable, Inject } from '@nestjs/common';
import { Model } from "mongoose"

import { DBCONSTANTS } from '@/database/db.constants';

import { ${interfaceClassName} } from './interface/${fileName}.interface';

@Injectable()
export class ${serviceClassName} {
  // TODO: set model for you. And after delete doc line
  @Inject(DBCONSTANTS.userModel)
  private readonly userModel: Model<${interfaceClassName}>

  constructor() { }

  // ...
}
`;

// dto/name.dto.ts
const dtoFile = `import { ApiModelProperty } from "@nestjs/swagger";

export class ${dtoClassName} {
  @ApiModelProperty({
    example: '示例值',
    description: '字段描述'
  })
  name: string

  // ...
}`;

// name.interface.ts
const interfaceFile = `import { Document } from "mongoose"

export interface ${interfaceClassName} extends Document {
  name: string
}`;

// name.providers.ts
const schemaProvidersFile = `import { Connection } from "mongoose"

import { DBCONSTANTS } from "@/database/db.constants"

import { ${schemaClassName} } from "./${fileName}.schema"

export const ${providersClassName} = [
  {
    // TODO: set model for you. And after delete doc line
    provide: DBCONSTANTS.userModel,
    // TODO: set User for you. And after delete doc line
    useFactory: (connection: Connection) => connection.model("User", ${schemaClassName}),
    inject: [DBCONSTANTS.databaseConnection]
  }
]`;

// name.schema.ts
const schemaFile = `import * as mongoose from "mongoose"

export const ${schemaClassName} = new mongoose.Schema({
  username: String,
})`;

// path
const moduleFilePath = `${basePath}/${newPathInput}.module.ts`;
const controllerFilePath = `${basePath}/${newPathInput}.controller.ts`;
const serviceFilePath = `${basePath}/${newPathInput}.service.ts`;
const dtoFilePath = `${basePath}/${pathInput}/dto/${fileName}.dto.ts`;
const interfaceFilePath = `${basePath}/${pathInput}/interface/${fileName}.interface.ts`;
const schemaProvidersFilePath = `${basePath}/${pathInput}/schema/${fileName}.providers.ts`;
const schemaFilePath = `${basePath}/${pathInput}/schema/${fileName}.schema.ts`;

// 生成
initFile(moduleFilePath, moduleFile);
initFile(controllerFilePath, controllerFile);
initFile(serviceFilePath, serviceFile);
initFile(dtoFilePath, dtoFile);
initFile(interfaceFilePath, interfaceFile);
initFile(schemaProvidersFilePath, schemaProvidersFile);
initFile(schemaFilePath, schemaFile);
